﻿namespace AlgoHero.Modelo
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Text;
    using System.Xml;

    public enum ctes{ MAX_NOTAS = 12 };

    public class Acorde : IEnumerable
    {
        #region Atributos

        private Figura figuraMusical;
        private IList notas;

        #endregion

        #region Constructor

        public Acorde()
        {
            /* Postcondición: crea un acorde vacío de duración musical 0.
             */
            this.notas = new List<Nota>();
            this.figuraMusical = Figura.NULA;
        }

        public Acorde(XmlElement nodo)
        {
            this.figuraMusical = Figura.NULA;
            this.notas = new List<Nota>();

            XmlNodeList listaNodoNota = nodo.ChildNodes;

            foreach (XmlElement nodoNota in listaNodoNota)
            {
                this.AddNota(new Nota(nodoNota));
            }
        }

        #endregion

        #region Métodos
              

        public void AddNota(Nota unaNota)
        {
            /* Agrega una nota al acorde. Si el acorde está lleno, lanza una excepcion del tipo
             * AcordeLlenoException. Si la duración de la nota es máxima asigna esta duración al 
             * acorde.
             * Postcondición: Lanza AcordeLlenoException si el acorde está lleno. Sino, lo agrega.
             */
            if ((ctes)this.Cantidad == ctes.MAX_NOTAS)
            {
                throw new AcordeLlenoException("No se pueden agregar mas notas al acorde.");
            }

            notas.Add(unaNota);
            if ((int)this.figuraMusical < (int)unaNota.FiguraMusical)
            {
                this.figuraMusical = unaNota.FiguraMusical;
            }         
        }

        public double CalcularDuracionFisica(double tiempoDeCompas, int duracionDeCompas)
        {
            /* Calcula el tiempo físico que dura el acorde y lo devuelve.
             * Precondición: tiempoDeCompas y duracionDeCompas deben ser no negativos.
             * Postcondicion: lanza ArgumentException si los argumentos son invalidos.
             */

            if (tiempoDeCompas < 0 || duracionDeCompas < 0)
            {
                throw new ArgumentException("Ingresó una duracion o tiempo de compás negativa");
            }
            double tiempoDeSemiCorchea = tiempoDeCompas / duracionDeCompas;
            return ((int)this.FiguraMusical * tiempoDeSemiCorchea);
        }

        public IEnumerator GetEnumerator()
        {
            /* Implementa un iterador para esta clase ("foreach") */

            for (int i = 0; i < this.notas.Count; i++)
            {
                yield return this.notas[i];
            }
        }

        internal void SerializarEn(XmlDocument documento, XmlNode nodoPadre)
        {
            XmlNode nodo = documento.CreateNode(XmlNodeType.Element, "Acorde", "");

            foreach (Nota nota in this.notas)
            {
                nota.SerializarEn(documento, nodo);
            }

            nodoPadre.AppendChild(nodo);
        }


        public bool EsVacio()
        {
            return (this.Cantidad == 0);
        }

        #endregion

        #region Propiedades

        public Figura FiguraMusical
        {
            get { return figuraMusical; }
            //set { figuraMusical = value; }
        }

        public int Cantidad
        {
            get { return this.notas.Count; }
        }

        #endregion
    }
}
